#pragma once

#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <sdl/sdl.hpp>

#define NK_INCLUDE_FIXED_TYPES
#define NK_INCLUDE_STANDARD_IO
#define NK_INCLUDE_STANDARD_VARARGS
#define NK_INCLUDE_DEFAULT_ALLOCATOR
#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
#define NK_INCLUDE_FONT_BAKING
#define NK_INCLUDE_DEFAULT_FONT
#include <nuklear/nuklear.h>
#include <nuklear/nuklear_sdl_gl3.h>

#include <vector>
#include <memory>
#include <functional>

#include "shader.h"
#include "texture.h"
#include "camera.h"
#include "utils.h"

#define MAX_VERTEX_MEMORY 512 * 1024
#define MAX_ELEMENT_MEMORY 128 * 1024

class IDrawable {
  public:
    void doinit() { init(); inited_ = true; };
    void douninit() { uninit(); inited_ = false; };
    bool inited() { return inited_; }
    virtual unsigned index() const = 0;
    virtual const char* title() const = 0;
    virtual const char* resPath() const = 0;
  public:
    virtual void init() {};
    virtual void uninit() {};
    virtual void update(float elapsedTime) {};
    virtual void render() {};
    virtual void imgui(nk_context *ctx, struct nk_rect &rc) {};
    virtual void processInput(sdl::event::Event &evt) {};
  protected:
    bool inited_;
};

namespace gl = sdl::gl;
extern sdl::Window::Wrapped window_;
extern gl::Context context_;
extern std::vector<std::shared_ptr<IDrawable>> draws_;
extern std::unique_ptr<nk::SDLContext> nkctx_;
extern void addCreateDrawable(std::function<std::shared_ptr<IDrawable>()> fun);
struct SettingData {
  nk_colorf bkcolor;
  GLenum polygonMode;
  int drawIndex, lastDrawIndex;
  SettingData();
};
extern SettingData setting_;

#define DECLARE_DRAWABLE_BEGIN(classname,respath, f,s,t) \
  namespace { \
    class classname : public IDrawable { \
      public: \
        virtual unsigned index() const override { return (f << 16 | s << 8 | t); } \
        virtual const char* title() const override { return respath " - " #classname; } \
        virtual const char* resPath() const override { return respath; }

#define DECLARE_DRAWABLE_END(classname) \
    }; \
    struct classname ## _struct { \
      classname ## _struct() { \
        addCreateDrawable([]() -> std::shared_ptr<IDrawable> { \
          return std::make_shared<classname>(); \
        }); \
      } \
    } __ ## classname; \
  }
